﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Research1
{
    public partial class Form1 : Form
    {
        bool showGrid;
        bool showGradients;
        int octaves = 1;

        struct Vector
        {
            private static Random generator = new Random();

            public override string ToString()
            {
                return "[" + x + "," + y + "]";
            }
            public Vector(float X, float Y)
            {
                x = X;
                y = Y;
            }

            public static Vector operator -(Vector a, Vector b)
            {
                return new Vector(a.x - b.x, a.y - b.y);
            }


            public static Vector Random()
            {
                Vector v = new Vector(((float)generator.NextDouble() - 0.5f) * 2, ((float)generator.NextDouble() - 0.5f) * 2);
                if (v.x == 0f && v.y == 0f)
                {
                    v.x = generator.Next(2) > 1 ? -0.5f : 0.5f;
                    v.y = generator.Next(2) > 1 ? -0.5f : 0.5f;
                }

                return v;
            }

            public static void Normalize(ref Vector v)
            {
                float l = (float)Math.Sqrt(v.x * v.x + v.y * v.y);
                if (l == 0.0f)
                    v.x = v.y = 0.0f;
                else
                {
                    v.x /= l;
                    v.y /= l;
                }
            }

            public static float Dot(ref Vector a, ref Vector b)
            {
                return (a.x * b.x + a.y * b.y);
            }

            public float x;
            public float y;
        }

        const int NOISE_DIM = 512;
        const int BASE_DATA_DIM = 4;

        Vector[] basedata = new Vector[BASE_DATA_DIM * BASE_DATA_DIM];
        int[] perms = new int[BASE_DATA_DIM];

        public Form1()
        {
            InitializeComponent();
            InitNoise();

            showGrid = toolStripButton1.Checked;
            showGradients = toolStripButton2.Checked;
        }



        Vector QueryBase(int x, int y)
        {

            //return basedata[(y + perms[x % BASE_DATA_DIM]) % BASE_DATA_DIM];

            return basedata[(x % BASE_DATA_DIM) + (BASE_DATA_DIM * (y%BASE_DATA_DIM))];
        }


        void InitNoise()
        {
            for (int x = 0; x < BASE_DATA_DIM * BASE_DATA_DIM; x++)
            {
                Vector v = Vector.Random();
                Vector.Normalize(ref v);
                basedata[x] = v;
            }

            Random generator = new Random();


            for (int x = 0; x < BASE_DATA_DIM; x++)
            {
                perms[x] = generator.Next(999) % BASE_DATA_DIM;
            }
        }

        float InterpolateCurve(float s)
        {
            s = 1f - s;
            return s * s * s * (s * (s * 6.0f - 15.0f) + 10.0f);
            //return (3.0f * s * s) - (2.0f * s * s * s);
        }
        float InterpolateLinear(float a, float b, float t)
        {
            return (a + (1f - t) * (b - a));
        }

        int Alpha(float a)
        {
            if (a > 1.0f)
                return 255;
            if (a < 0f)
                return 0;
            return (int)(a * 255f);
        }

        float Noise(Vector coordinate, Graphics g)
        {
            float x = coordinate.x - (float)Math.Floor(coordinate.x);
            float y = coordinate.y - (float)Math.Floor(coordinate.y);

            int q_leftc = (int)(x * BASE_DATA_DIM);
            int q_rightc = (q_leftc + 1);
            int q_topc = (int)(y * BASE_DATA_DIM);
            int q_bottomc = (q_topc + 1);

            Vector q_tl = QueryBase(q_leftc, q_topc);
            Vector q_tr = QueryBase(q_rightc, q_topc);
            Vector q_bl = QueryBase(q_leftc, q_bottomc);
            Vector q_br = QueryBase(q_rightc, q_bottomc);

            float q_leftf = (float)q_leftc / (float)BASE_DATA_DIM;
            float q_rightf = (float)q_rightc / (float)BASE_DATA_DIM;
            float q_topf = (float)q_topc / (float)BASE_DATA_DIM;
            float q_bottomf = (float)q_bottomc / (float)BASE_DATA_DIM;
                                    
            float t_leftx = x - q_leftf;
            float t_rightx = t_leftx - (1f / (float)BASE_DATA_DIM);
            float t_topy = y - q_topf;
            float t_bottomy = t_topy - (1f / (float)BASE_DATA_DIM);

            float v_tl = (q_tl.x * t_leftx + q_tl.y * t_topy) * (float)BASE_DATA_DIM;
            float v_tr = (q_tr.x * t_rightx + q_tr.y * t_topy) * (float)BASE_DATA_DIM;
            float v_bl = (q_bl.x * t_leftx + q_bl.y * t_bottomy) * (float)BASE_DATA_DIM;
            float v_br = (q_br.x * t_rightx + q_br.y * t_bottomy) * (float)BASE_DATA_DIM;

            float icX = InterpolateCurve(t_leftx * BASE_DATA_DIM);
            float icY = InterpolateCurve(t_topy * BASE_DATA_DIM);

            float v0 = InterpolateLinear(v_tl, v_tr, icX);
            float v1 = InterpolateLinear(v_bl, v_br, icX);

            float vf = InterpolateLinear(v0, v1, icY);

            //vf = (vf * 0.5f) + (1f / (float)BASE_DATA_DIM);
            //vf = vf * (float)BASE_DATA_DIM;
            //vf = vf + 0.25f;
            return vf ;
            //vf = (vf + 0.5f) / 2f;
            
            
            //vf *= 2;
            //vf *= 4f;

            //vf = icX * icY;
            //skip++;
            //if (skip % (5000) == 0)
            //{
            //    g.DrawLine(new Pen(Color.FromArgb(Alpha(v_br), Color.Blue)),
            //        new Point((int)(x * NOISE_DIM), (int)(y * NOISE_DIM)),
            //        new Point((int)(q_rightf * NOISE_DIM), (int)(q_bottomf * NOISE_DIM)));

            //    g.DrawLine(new Pen(Color.FromArgb(Alpha(v_bl), Color.Green)),
            //        new Point((int)(x * NOISE_DIM), (int)(y * NOISE_DIM)),
            //        new Point((int)(q_leftf * NOISE_DIM), (int)(q_bottomf * NOISE_DIM)));

            //    g.DrawLine(new Pen(Color.FromArgb(Alpha(v_tr), Color.Red)),
            //        new Point((int)(x * NOISE_DIM), (int)(y * NOISE_DIM)),
            //        new Point((int)(q_rightf * NOISE_DIM), (int)(q_topf * NOISE_DIM)));

            //    g.DrawLine(new Pen(Color.FromArgb(Alpha(v_tl), Color.Orange)),
            //        new Point((int)(x * NOISE_DIM), (int)(y * NOISE_DIM)),
            //        new Point((int)(q_leftf * NOISE_DIM), (int)(q_topf * NOISE_DIM)));
            //}


            //vf = Math.Min(1f, Math.Max(0f, vf));

            //int v = (int)(vf * 255.0);
            //return Color.FromArgb(v, v, v);
        }

        bool requestReload = true;
        Bitmap bmp = new Bitmap(NOISE_DIM, NOISE_DIM);

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);

            if (requestReload)
            {

                Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
                System.Drawing.Imaging.BitmapData bmpData =
                    bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
                    System.Drawing.Imaging.PixelFormat.Format24bppRgb);

                IntPtr ptr = bmpData.Scan0;

                int bytes = bmpData.Stride * bmp.Height;
                byte[] rgbValues = new byte[bytes];

                System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
                toolStripProgressBar1.Minimum = 0;
                toolStripProgressBar1.Maximum = rgbValues.Length;

                for (int counter = 0, i = 0; counter < rgbValues.Length; counter += 3, i++)
                {
                    if (counter % 1100 == 0)
                        toolStripProgressBar1.Value = counter;

                    float x = (float)(i % NOISE_DIM) / (float)NOISE_DIM;
                    float y = (float)((int)(i / NOISE_DIM)) / (float)NOISE_DIM;

                    float v = 1.0f;
                    float res = 1.0f;
                    for (int o = 0; o < octaves; o++)
                    {
                        res *= 2f;
                        float amp = (float)Math.Pow(2.0, (double)o);
                        float n = Noise(new Vector(x * res, y * res), e.Graphics);
                        v += (n  * (1f / amp));

                    }

                    //v /= octaves;

                    //v += 1f;
                    v *= 0.5f;
                    v = Math.Min(1f, Math.Max(0f, v));

                    int vi = (int)(v * 255.0);
                    Color c = Color.FromArgb(vi, vi, vi);

                    rgbValues[counter + 0] = c.B;
                    rgbValues[counter + 1] = c.G;
                    rgbValues[counter + 2] = c.R;
                }

                System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);

                bmp.UnlockBits(bmpData);
                requestReload = false;
            }

            e.Graphics.DrawImage(bmp, 0, 0);
            ClientSize = new Size(NOISE_DIM, NOISE_DIM);

            Pen pen = new Pen(Color.FromArgb(100,244,0,0));
            pen.Width = 1;
            if (showGrid)
            {
                for (int x = 0; x < BASE_DATA_DIM; x++)
                {
                    e.Graphics.DrawLine(pen, new Point(x * (NOISE_DIM / BASE_DATA_DIM), 0), new Point(x * (NOISE_DIM / BASE_DATA_DIM), NOISE_DIM));
                }
                for (int y = 0; y < BASE_DATA_DIM; y++)
                {
                    e.Graphics.DrawLine(pen, new Point(0, y * (NOISE_DIM / BASE_DATA_DIM)), new Point(NOISE_DIM, y * (NOISE_DIM / BASE_DATA_DIM)));
                }
            }
            pen = new Pen(Color.FromArgb(100, 00, 0, 244));
            pen.Width = 1;
            if (showGradients)
            {
                for (int x = 0; x < BASE_DATA_DIM; x++)
                    for (int y = 0; y < BASE_DATA_DIM; y++)
                    {
                        const int VECTOR_SCALE = 15;
                        Vector v = QueryBase(x, y);
                        Vector.Normalize(ref v);

                        Point offset = new Point((int)(0.5 * NOISE_DIM / BASE_DATA_DIM), (int)(0.5 * NOISE_DIM / BASE_DATA_DIM));

                        Point origin = new Point(offset.X + (x * (NOISE_DIM / BASE_DATA_DIM)), offset.Y + (y * (NOISE_DIM / BASE_DATA_DIM)));
                        Point end = new Point(offset.X + (int)(v.x * VECTOR_SCALE) + (x * (NOISE_DIM / BASE_DATA_DIM)), offset.Y + (int)(v.y * VECTOR_SCALE) + (y * (NOISE_DIM / BASE_DATA_DIM)));

                        e.Graphics.DrawLine(pen, origin, end);
                        e.Graphics.DrawEllipse(pen, origin.X - 1, origin.Y - 1, 2, 2);
                    }
            }
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            this.toolStripButton1.Checked = !this.toolStripButton1.Checked;
            showGrid = this.toolStripButton1.Checked;
            Refresh();
        }

        private void toolStripMenuItem2_Click(object sender, EventArgs e)
        {
            if (octaves != 1)
            {
                octaves = 1;
                requestReload = true;
                Refresh();
            }
        } 
        private void toolStripMenuItem3_Click(object sender, EventArgs e)
        {
            if (octaves != 2)
            {
                octaves = 2;
                requestReload = true;
                Refresh();
            }
        }

        private void toolStripMenuItem4_Click(object sender, EventArgs e)
        {
            if (octaves != 3)
            {
                octaves = 3;
                requestReload = true;
                Refresh();
            }
        }

        private void toolStripMenuItem5_Click(object sender, EventArgs e)
        {
            if (octaves != 4)
            {
                octaves = 4;
                requestReload = true;
                Refresh();
            }
        }

        private void toolStripMenuItem6_Click(object sender, EventArgs e)
        {
            if (octaves != 5)
            {
                octaves = 5;
                requestReload = true;
                Refresh();
            }

        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            this.toolStripButton2.Checked = !this.toolStripButton2.Checked;
            showGradients = this.toolStripButton2.Checked;
            Refresh();
        }

        private void toolStripButton3_Click(object sender, EventArgs e)
        {
            InitNoise();
            requestReload = true;
            Refresh();
        }

        
    }
}
